/*
 * Copyright 2007-2009 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 * 
 * Project: gtinyurl
 */
package org.gnut.tinyurl.portlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.GenericPortlet;
import javax.portlet.PortletConfig;
import javax.portlet.PortletException;
import javax.portlet.PortletSession;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.apache.portals.bridges.common.ServletContextProvider;
import org.gnut.tinyurl.portlet.servlet.RequestResponseWrapperFactory;
import org.gnut.tinyurl.portlet.servlet.TinyHttpServletRequest;
import org.gnut.tinyurl.portlet.servlet.TinyHttpServletResponse;
import org.gnut.tinyurl.portlet.utils.SingletonUtils;

public class TinyPortlet extends GenericPortlet {
	public static final String __tiny_uri__ = "__tiny_uri__";

	public static final String __tiny_multipart_content__ = "__tiny_multipart_content__";
	public static final String __tiny_params_session__ = "__tiny_params_session__";
	public static final String __old_tiny_url__ = "__old_tiny_url__";
	public static final String __old_tiny_mode__ = "__old_tiny_mode__";

	public static final String portlet_mode = "__portlet_mode__";

	private String defaultViewURI = "/WEB-INF/tinyurl/view.jsp";

	private String defaultEditURI = "/WEB-INF/tinyurl/edit.jsp";

	private String defaultHelpURI = "/WEB-INF/tinyurl/help.jsp";

	private String defaultRequestResponseWrapperFactory = "org.gnut.tinyurl.portlet.servlet.DefaultRequestResponseWrapperFactory";

	private ServletContextProvider contextProvider;
	private RequestResponseWrapperFactory requestResponseWrapperFactory;

	/**
	 * 
	 * @return
	 */
	public String getDefaultViewURI() {
		return defaultViewURI;
	}

	/**
	 * 
	 * @param defaultViewURI
	 */
	public void setDefaultViewURI(String defaultViewURI) {
		this.defaultViewURI = defaultViewURI;
	}

	/**
	 * 
	 * @return
	 */
	public String getDefaultEditURI() {
		return defaultEditURI;
	}

	/**
	 * 
	 * @param defaultEditURI
	 */
	public void setDefaultEditURI(String defaultEditURI) {
		this.defaultEditURI = defaultEditURI;
	}

	/**
	 * 
	 * @return
	 */
	public String getDefaultHelpURI() {
		return defaultHelpURI;
	}

	/**
	 * 
	 * @param defaultHelpURI
	 */
	public void setDefaultHelpURI(String defaultHelpURI) {
		this.defaultHelpURI = defaultHelpURI;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.portlet.GenericPortlet#init(javax.portlet.PortletConfig)
	 */
	public void init(PortletConfig config) throws PortletException {
		contextProvider = (ServletContextProvider) SingletonUtils
				.getInstance(config
						.getInitParameter("tiny.servletContextProvider"));
		if (contextProvider == null)
			throw new PortletException(
					"Can't create ServletContextProvider instance with class :"
							+ config
									.getInitParameter("tiny.servletContextProvider"));
		defaultEditURI = config.getInitParameter("tiny.editMode") == null ? defaultEditURI
				: config.getInitParameter("tiny.editMode");
		defaultHelpURI = config.getInitParameter("tiny.helpMode") == null ? defaultHelpURI
				: config.getInitParameter("tiny.helpMode");
		defaultViewURI = config.getInitParameter("tiny.viewMode") == null ? defaultViewURI
				: config.getInitParameter("tiny.viewMode");
		requestResponseWrapperFactory = (RequestResponseWrapperFactory) SingletonUtils
				.getInstance(config
						.getInitParameter("tiny.requestResponseWrapperFactory") == null ? defaultRequestResponseWrapperFactory
						: config
								.getInitParameter("tiny.requestResponseWrapperFactory"));
		super.init(config);
	}

	/**
	 * 
	 * @param request
	 * @param response
	 * @param defaulUri
	 * @throws PortletException
	 * @throws IOException
	 */
	public void processRequest(RenderRequest request, RenderResponse response,
			String defaulUri) throws PortletException, IOException {
		HttpServletRequest httpServletRequest = contextProvider
				.getHttpServletRequest(this, request);
		HttpServletResponse httpServletResponse = contextProvider
				.getHttpServletResponse(this, response);
		if (httpServletRequest == null || httpServletResponse == null) {
			throw new PortletException(
					"Can't create request and respone by ServletContextProvider");
		}

		if ("true".equals(request.getPortletSession().getAttribute(
				__tiny_multipart_content__))) {

			final FileItem fileItem = (FileItem) request.getPortletSession()
					.getAttribute("tiny.inputStream");
			request.getPortletSession().setAttribute("tiny.inputStream", null);
			Map params = (Map) request.getPortletSession().getAttribute(
					__tiny_params_session__);
			TinyHttpServletRequest wrapper = new TinyHttpServletRequest(
					httpServletRequest, request, params);
			final InputStream inputStream = fileItem.getInputStream();
			wrapper.setInputStream(inputStream);
			wrapper.setContentType((String) request.getPortletSession()
					.getAttribute("tiny.contentType"));
			request.getPortletSession().setAttribute("tiny.contentType", null);
			wrapper.setMethod((String) request.getPortletSession()
					.getAttribute("tiny.method"));
			request.getPortletSession().setAttribute("tiny.method", null);
			request.getPortletSession().setAttribute(
					__tiny_multipart_content__, null);

			if (ServletFileUpload.isMultipartContent(wrapper)) {
				String uri = null;
				// Create a factory for disk-based file items
				FileItemFactory factory = new DiskFileItemFactory();

				// Create a new file upload handler
				ServletFileUpload upload = new ServletFileUpload(factory);

				// Parse the request
				try {
					List items = upload.parseRequest(wrapper);
					boolean found = false;
					for (Iterator iterator = items.iterator(); iterator
							.hasNext()
							&& !found;) {
						FileItem fileItem2 = (FileItem) iterator.next();
						if (fileItem2.isFormField()
								&& __tiny_uri__
										.equals(fileItem2.getFieldName())) {
							uri = fileItem2.getString();
							found = true;
						}
					}
					wrapper.setInputStream(fileItem.getInputStream());

					uri = (uri == null) ? defaulUri : uri;

					uri = (uri != null && uri.startsWith("/")) ? uri : "/"
							+ uri;
					if (requestResponseWrapperFactory != null) {
						httpServletRequest = requestResponseWrapperFactory
								.createRequest(
										httpServletRequest,
										contextProvider.getServletContext(this),
										uri, request, response);
						httpServletResponse = requestResponseWrapperFactory
								.createResponse(
										httpServletResponse,
										contextProvider.getServletContext(this),
										uri, request, response);
					}
					httpServletResponse = new TinyHttpServletResponse(
							httpServletResponse, httpServletRequest, response);

					wrapper.setContextPath(request.getContextPath());
					wrapper.getRequestDispatcher(uri).include(wrapper,
							httpServletResponse);

				} catch (FileUploadException e) {
					throw new PortletException(e);
				} catch (ServletException e) {
					throw new PortletException(e);
				} finally {
					fileItem.delete();
				}
			}

		} else {
			String uri;
			PortletSession portletSession = request.getPortletSession();
			String old_url = (String) portletSession
					.getAttribute(__old_tiny_url__);
			String old_mode = (String) portletSession
					.getAttribute(__old_tiny_mode__);
			String url = request.getParameter(__tiny_uri__);

			String mode = request.getParameter(portlet_mode);

			if (url == null || "".equals(url) || mode == null
					|| "".equals(mode)) {
				uri = defaulUri;
			} else if (url.equals(old_url) && mode.equals(old_mode)) {
				uri = defaulUri;
			} else {
				uri = url;
				portletSession.setAttribute(__old_tiny_url__, url);
				portletSession.setAttribute(__old_tiny_mode__, mode);
			}

			uri = uri.startsWith("/") ? uri : "/" + uri;
			if (requestResponseWrapperFactory != null) {
				httpServletRequest = requestResponseWrapperFactory
						.createRequest(httpServletRequest, contextProvider
								.getServletContext(this), uri, request,
								response);
				httpServletResponse = requestResponseWrapperFactory
						.createResponse(httpServletResponse, contextProvider
								.getServletContext(this), uri, request,
								response);
			}
			Map params = (Map) request.getPortletSession().getAttribute(
					__tiny_params_session__);
			request.setAttribute(__tiny_params_session__, null);

			httpServletResponse = new TinyHttpServletResponse(
					httpServletResponse, httpServletRequest, response);
			try {

				TinyHttpServletRequest wrapper = new TinyHttpServletRequest(
						httpServletRequest, request, params);
				wrapper.setContextPath(request.getContextPath());
				wrapper.getRequestDispatcher(uri).include(wrapper,
						httpServletResponse);
			} catch (ServletException e) {
				throw new PortletException(e);
			}
		}

	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.portlet.GenericPortlet#doView(javax.portlet.RenderRequest,
	 * javax.portlet.RenderResponse)
	 */
	protected void doView(RenderRequest request, RenderResponse response)
			throws PortletException, IOException {
		processRequest(request, response, defaultViewURI);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.portlet.GenericPortlet#doEdit(javax.portlet.RenderRequest,
	 * javax.portlet.RenderResponse)
	 */
	protected void doEdit(RenderRequest request, RenderResponse response)
			throws PortletException, IOException {
		processRequest(request, response, defaultEditURI);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see javax.portlet.GenericPortlet#doHelp(javax.portlet.RenderRequest,
	 * javax.portlet.RenderResponse)
	 */
	protected void doHelp(RenderRequest request, RenderResponse response)
			throws PortletException, IOException {
		processRequest(request, response, defaultHelpURI);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * javax.portlet.GenericPortlet#processAction(javax.portlet.ActionRequest,
	 * javax.portlet.ActionResponse)
	 */
	public void processAction(ActionRequest request, ActionResponse response)
			throws PortletException, IOException {
		HttpServletRequest httpServletRequest = contextProvider
				.getHttpServletRequest(this, request);
		HttpServletResponse httpServletResponse = contextProvider
				.getHttpServletResponse(this, response);
		if (httpServletRequest == null || httpServletResponse == null) {
			throw new PortletException(
					"Can't create request and respone by ServletContextProvider");
		} else {
			if (ServletFileUpload.isMultipartContent(httpServletRequest)) {

				request.getPortletSession().setAttribute(
						__tiny_multipart_content__, "true");
				FileItemFactory factory = new DiskFileItemFactory();
				FileItem fileItem = factory.createItem("tiny.field",
						httpServletRequest.getContentType(), false,
						"tiny.fileName");
				OutputStream outputStream = fileItem.getOutputStream();
				InputStream inputStream = httpServletRequest.getInputStream();
				byte[] buffer = new byte[1024 * 1024];
				int l = inputStream.read(buffer);
				while (l > 0) {
					outputStream.write(buffer, 0, l);
					l = inputStream.read(buffer);
				}
				request.getPortletSession().setAttribute("tiny.inputStream",
						fileItem);

				request.getPortletSession().setAttribute("tiny.contentType",
						httpServletRequest.getContentType());
				request.getPortletSession().setAttribute("tiny.method",
						httpServletRequest.getMethod());

			} else {
				String redirectUri = request.getParameter("__send_redirect__");
				Map map = request.getParameterMap();
				Map map2 = new HashMap();
				map2.putAll(map);
				if ("post".equalsIgnoreCase(httpServletRequest.getMethod())) {
					request.getPortletSession().setAttribute(
							__tiny_params_session__, map);
					response.setRenderParameter(__tiny_uri__, redirectUri);
				} else {
					request.getPortletSession().setAttribute(
							__tiny_params_session__, null);
					map2.put(__tiny_uri__, new String[] { redirectUri });
					response.setRenderParameters(map2);
				}
				response.setRenderParameter(portlet_mode, request
						.getPortletMode().toString());

			}
		}

	}

}
